/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.system.auth.util;

import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import me.ijleex.mgmt.commons.util.snowflake.IdWorker;
import me.ijleex.mgmt.framework.json.util.JacksonUtils;
import me.ijleex.mgmt.system.auth.model.JwtUser;

/**
 * 令牌（Auth0-Jwt）工具
 *
 * @author liym
 * @since 2019-12-20 21:51 新建
 */
public final class JwtUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JwtUtil.class);

    /**
     * Token 有效时间，默认30分钟
     */
    public static final long JWT_TTL = 30 * 60 * 1000;

    /**
     * 密钥生成算法
     */
    private static final String KEY_GEN_ALG = "HMacSHA256";

    private static final JWTCreator.Builder JWT_BUILDER;
    private static final JWTVerifier JWT_VERIFIER;
    private static final Algorithm ALGORITHM;

    private JwtUtil() {}

    static {
        final KeyGenerator keyGen;
        try {
            keyGen = KeyGenerator.getInstance(KEY_GEN_ALG);
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("init JwtUtil error", e);
            throw new IllegalStateException("init JwtUtil error");
        }

        SecretKey secretKey = keyGen.generateKey();
        byte[] encodedKey = secretKey.getEncoded();

        // 算法
        ALGORITHM = Algorithm.HMAC256(encodedKey);

        JWT_BUILDER = JWT.create();
        JWT_VERIFIER = JWT.require(ALGORITHM).build();
    }

    /**
     * 生成 Token
     *
     * @param userId 用户 ID
     * @param username 用户名
     * @return Token
     * @see com.auth0.jwt.impl.PublicClaims
     * @see JwtUser
     * @since 2019-12-20 21:54
     */
    public static String createToken(final long userId, final String username) {
        Date date = new Date();
        return JWT_BUILDER
                // JWT的唯一身份标识，主要用来作为一次性Token，从而回避重放攻击
                .withJWTId(Long.toString(IdWorker.getId()))
                // JWT签发者（签名是由谁生成）
                .withIssuer(Long.toString(userId))
                // 主题（JWT面向的用户）
                .withSubject(username)
                // 签名过期的时间（两小时）
                .withExpiresAt(DateUtils.addHours(date, 2))
                // 定义在什么时间之前，该JWT不可用
                .withNotBefore(date)
                // JWT签发时间
                .withIssuedAt(date)
                // 签名的观众（谁接受签名）
                .withAudience("APP")
                .sign(ALGORITHM);
    }

    /**
     * 验证 Token
     *
     * @param token Token
     * @return true/false
     * @since 2019-12-20 23:48
     */
    public static boolean verifyToken(final String token) {
        try {
            JWT_VERIFIER.verify(token);
            return true;
        } catch (Exception e) {
            // 鉴权失败
            LOGGER.error("verifyToken error", e);
            return false;
        }
    }

    /**
     * 解析 Token
     *
     * @param token Token
     * @return 用户信息
     * @since 2019-12-21 16:51
     */
    public static JwtUser parseToken(final String token) {
        DecodedJWT jwt;
        try {
            jwt = JWT_VERIFIER.verify(token);
        } catch (Exception e) {
            // 鉴权失败
            LOGGER.error("parseToken error", e);
            return null;
        }
        String payload = jwt.getPayload();

        byte[] decodedBytes = Base64.getDecoder().decode(payload);
        String payloadJson = new String(decodedBytes, StandardCharsets.UTF_8);
        return JacksonUtils.jsonToObj(payloadJson, JwtUser.class);
    }

}
